﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO.Compression;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace 运动控制卡Demo
{
    // 指令返回值：
    // 0 指令执行成功 无
    // 1 指令执行错误 1. 检查当前指令的执行条件是否满足
    // 2 license 不支持 1. 如果需要此功能，请与生产厂商联系。
    // 7 指令参数错误 1．检查当前指令输入参数的取值
    // 8 不支持该指令 DSP 固件不支持该指令对应的功能

    public partial class FrmMain : Form
    {
        // 轴号
        public short axis { get => Convert.ToInt16(comboBox1.Text); }
        // 指令返回值
        short rtn;

        public FrmMain()
        {
            InitializeComponent();
            // 默认选中轴1
            // comboBox1.SelectedIndex = 0;
        }

        #region 窗体的加载事件
        private void FrmMain_Load(object sender, EventArgs e)
        {
            
        }
        #endregion


        #region 初始化
        /// <summary>
        /// 初始化
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnInit_Click(object sender, EventArgs e)
        {
            // 打开控制卡
            rtn = gts.mc.GT_Open(0, 1);
            if (rtn != 0)
            {
                Console.WriteLine("打开运动控制卡失败！");
                return;
            }
            // 控制卡复位
            rtn = gts.mc.GT_Reset();
            // 加载配置文件
            rtn = gts.mc.GT_LoadConfig("GT800_text.cfg");
            // 清除状态
            rtn = gts.mc.GT_ClrSts(1, 8);
            // 开启定时器
            timer1.Enabled = true;
            // 检测轴状态
            comboBox1_SelectedIndexChanged(null,null);
        }
        /// <summary>
        /// 清除状态
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnClrSts_Click(object sender, EventArgs e)
        {
            // 清除状态
            rtn = gts.mc.GT_ClrSts(1, 8);
        }
        /// <summary>
        /// 下拉框
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            bool bFlagAlarm = false;			// 伺服报警标志
            bool bFlagMError = false;			// 跟随误差越限标志
            bool bFlagPosLimit = false;		// 正限位触发标志
            bool bFlagNegLimit = false;		// 负限位触发标志
            bool bFlagSmoothStop = false;		// 平滑停止标志
            bool bFlagAbruptStop = false;		// 急停标志
            bool bFlagServoOn = false;		// 伺服使能标志
            bool bFlagMotion = false;			// 规划器运动标志
            int lAxisStatus;					// 轴状态
            uint clk;
            // 读取轴状态
            rtn = gts.mc.GT_GetSts(axis, out lAxisStatus, 1, out clk);
            // 伺服报警标志
            if ((lAxisStatus & 0x2) != 0)
            {
                bFlagAlarm = true;
                Console.WriteLine("伺服报警\n");
            }
            else
            {
                bFlagAlarm = false;
                Console.WriteLine("伺服正常\n");
            }
            // 跟随误差越限标志
            if ((lAxisStatus & 0x10) != 0)
            {
                bFlagMError = true;
                Console.WriteLine("运动出错\n");
            }
            else
            {
                bFlagMError = false;
                Console.WriteLine("运动正常\n");
            }
            // 正向限位
            if ((lAxisStatus & 0x20) != 0)
            {
                bFlagPosLimit = true;
                Console.WriteLine("正限位触发\n");
            }
            else
            {
                bFlagPosLimit = false;
                Console.WriteLine("正限位未触发\n");
            }
            // 负向限位
            if ((lAxisStatus & 0x40) != 0)
            {
                bFlagNegLimit = true;
                Console.WriteLine("负限位触发\n");
            }
            else
            {
                bFlagNegLimit = false;
                Console.WriteLine("负限位未触发\n");
            }
            // 平滑停止
            if ((lAxisStatus & 0x80) != 0)
            {
                bFlagSmoothStop = true;
                Console.WriteLine("平滑停止触发\n");
            }
            else
            {
                bFlagSmoothStop = false;
                Console.WriteLine("平滑停止未触发\n");
            }
            // 急停标志
            if ((lAxisStatus & 0x100) != 0)
            {
                bFlagAbruptStop = true;
                Console.WriteLine("急停触发\n");
            }
            else
            {
                bFlagAbruptStop = false;
                Console.WriteLine("急停未触发\n");
            }
            // 伺服使能标志
            if ((lAxisStatus & 0x200) != 0)
            {
                bFlagServoOn = true;
                Console.WriteLine("伺服使能\n");
            }
            else
            {
                bFlagServoOn = false;
                Console.WriteLine("伺服关闭\n");
            }
            // 规划器正在运动标志
            if ((lAxisStatus & 0x400) != 0)
            {
                bFlagMotion = true;
                Console.WriteLine("规划器正在运动\n");
            }
            else
            {
                bFlagMotion = false;
                Console.WriteLine("规划器已停止\n");
            }
            // 修改状态
            if (bFlagServoOn)
            {
                btnEnable.Text = "伺服使能状态";
                btnEnable.BackColor = Color.Green;
            }
            else
            {
                btnEnable.Text = "伺服关闭状态";
                btnEnable.BackColor = Color.Gray;
            }
            // 剩下七个状态参照官方示例自行完成
            // ……
        }
        /// <summary>
        /// 伺服使能
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnEnable_Click(object sender, EventArgs e)
        {
            if (btnEnable.Text == "伺服使能状态")
            {
                rtn = gts.mc.GT_AxisOff(axis);
            }
            else
            {
                rtn = gts.mc.GT_AxisOn(axis);
            }
            comboBox1_SelectedIndexChanged(null, null);
        }
        /// <summary>
        /// 位置清零
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnPosZero_Click(object sender, EventArgs e)
        {
            // 位置清零
            rtn = gts.mc.GT_ZeroPos(1,8);
        }
        #endregion


        #region 点位运动
        private void btnTrap_Click(object sender, EventArgs e)
        {
            // 设置当前轴的运动模式为点位运动
            rtn = gts.mc.GT_PrfTrap(axis);

        }

        int pos, vel;
        private void btnTrapRun_Click(object sender, EventArgs e)
        {
            // 定义点位运动结构体变量
            gts.mc.TTrapPrm trapprm;
            rtn = gts.mc.GT_GetTrapPrm(axis,out trapprm);
            // 设置加/减速度
            trapprm.acc = 0.1;
            trapprm.dec = 0.1;
            // 设置平滑时间
            trapprm.smoothTime = 1;
            // 设置点位运动参数
            rtn = gts.mc.GT_SetTrapPrm(axis,ref trapprm);
            // 累加位置偏移
            pos += Convert.ToInt32(txtTrapDis.Text);
            // 点位运动速度
            vel = Convert.ToInt32(txtTrapV.Text);
            // 设置点位距离和速度
            rtn = gts.mc.GT_SetPos(axis, pos);
            rtn = gts.mc.GT_SetVel(axis, vel);
            // 开启点位运动
            rtn = gts.mc.GT_Update(axis);
        }
        #endregion


        #region 点动
        private void btnJog_Click(object sender, EventArgs e)
        {
            // 设置为jog模式
            rtn = gts.mc.GT_PrfJog(axis);
        }
        // 点动的结构体变量
        gts.mc.TJogPrm jog;
        private void btnFu_MouseDown(object sender, MouseEventArgs e)
        {
            jog.acc = 0.1;
            jog.dec = 0.1;
            // 设置jog运动参数
            gts.mc.GT_SetJogPrm(axis,ref jog);
            // 设置目标速度
            gts.mc.GT_SetVel(axis,Convert.ToDouble(txtJogV.Text));
            //更新轴运动
            gts.mc.GT_Update(1 << (axis - 1));
        }
        private void btnFu_MouseUp(object sender, MouseEventArgs e)
        {
            gts.mc.GT_Stop(1 << (axis - 1), 0);
        }
        private void btnZheng_MouseDown(object sender, MouseEventArgs e)
        {
            jog.acc = 0.1;
            jog.dec = 0.1;
            // 设置jog运动参数
            gts.mc.GT_SetJogPrm(axis, ref jog);
            // 设置目标速度
            gts.mc.GT_SetVel(axis, -Convert.ToDouble(txtJogV.Text));
            //更新轴运动
            gts.mc.GT_Update(1 << (axis - 1));
        }
        private void btnZheng_MouseUp(object sender, MouseEventArgs e)
        {
            gts.mc.GT_Stop(1 << (axis - 1), 0);
        }
        #endregion


        #region 停止运动
        private void btnStop_Click(object sender, EventArgs e)
        {
            // 停止运动
            rtn = gts.mc.GT_Stop(1,0);
        }
        #endregion


        #region 定时器事件
        private void timer1_Tick(object sender, EventArgs e)
        {
            // 获取位置和速度
            getPosAndVel();
            // 获取DO状态
            getDoSts();
            // 读取DI状态
            getDISts();
        }
        double prfpos, prfvel, encpos, encvel;
        uint clk;
        // 获取位置和速度
        private void getPosAndVel()
        {
            // 读取规划位置和实际位置
            rtn = gts.mc.GT_GetPrfPos(axis, out prfpos, 1, out clk);
            rtn = gts.mc.GT_GetEncPos(axis, out encpos, 1, out clk);
            txtGHPos.Text = prfpos.ToString();
            txtSJPos.Text = encpos.ToString();
            // 读取规划速度和实际速度
            rtn = gts.mc.GT_GetPrfVel(axis, out prfvel, 1, out clk);
            rtn = gts.mc.GT_GetEncVel(axis, out encvel, 1, out clk);
            txtGHV.Text = prfvel.ToString();
            txtSJV.Text = encvel.ToString();
        }
        // 获取DO状态
        private void getDoSts()
        {
            // 状态
            int pValue;
            // 读取数字 IO 输出状态
            rtn = gts.mc.GT_GetDo(gts.mc.MC_GPO, out pValue);
            // Console.WriteLine(pValue);
            // Console.WriteLine(Convert.ToString(pValue,2));
            if (Convert.ToBoolean(pValue & (1 << 0))) {
                ucSignalLamp1.LampColor = new Color[] { Color.FromArgb(0, 255, 0) };
            }
            else
            {
                ucSignalLamp1.LampColor = new Color[] { Color.FromArgb(128, 128, 128) }; 
            }
            if (Convert.ToBoolean(pValue & (1 << 1)))
            {
                ucSignalLamp2.LampColor = new Color[] { Color.FromArgb(0, 255, 0) };
            }
            else
            {
                ucSignalLamp2.LampColor = new Color[] { Color.FromArgb(128, 128, 128) }; 
            }
            if (Convert.ToBoolean(pValue & (1 << 2)))
            {
                ucSignalLamp5.LampColor = new Color[] { Color.FromArgb(0, 255, 0) };
            }
            else
            {
                ucSignalLamp5.LampColor = new Color[] { Color.FromArgb(128, 128, 128) };
            }
            if (Convert.ToBoolean(pValue & (1 << 3)))
            {
                ucSignalLamp6.LampColor = new Color[] { Color.FromArgb(0, 255, 0) };
            }
            else
            {
                ucSignalLamp6.LampColor = new Color[] { Color.FromArgb(128, 128, 128) };
            }
            if (Convert.ToBoolean(pValue & (1 << 4)))
            {
                ucSignalLamp7.LampColor = new Color[] { Color.FromArgb(0, 255, 0) };
            }
            else
            {
                ucSignalLamp7.LampColor = new Color[] { Color.FromArgb(128, 128, 128) };
            }
        }
        // 读取DI状态
        private void getDISts()
        {
            // 状态
            int pValue;
            // 读取数字 IO 输出状态
            rtn = gts.mc.GT_GetDi(gts.mc.MC_GPI, out pValue);
            if (Convert.ToBoolean(pValue & (1 << 0)))
            {
                ucSignalLamp4.LampColor = new Color[] { Color.FromArgb(0, 255, 0) };
            }
            else
            {
                ucSignalLamp4.LampColor = new Color[] { Color.FromArgb(128, 128, 128) };
            }
            if (Convert.ToBoolean(pValue & (1 << 1)))
            {
                ucSignalLamp3.LampColor = new Color[] { Color.FromArgb(0, 255, 0) };
            }
            else
            {
                ucSignalLamp3.LampColor = new Color[] { Color.FromArgb(128, 128, 128) };
            }
        }
        #endregion


        #region 插补运动
        /// <summary>
        /// 建立坐标系
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSetCrd_Click(object sender, EventArgs e)
        {
            // TCrdPrm结构体变量，该结构体定义了坐标系
            gts.mc.TCrdPrm crdPrm = new gts.mc.TCrdPrm();
            // 为结构体赋值
            crdPrm.dimension = 2; // 坐标系为二维坐标系            crdPrm.synVelMax = 500; // 最大合成速度：500pulse/ms
            crdPrm.synAccMax = 1; // 最大加速度：1pulse/ms^2
            crdPrm.evenTime = 50; // 最小匀速时间：50ms
            crdPrm.profile1 = 1; // 规划器1(即profile1)对应到X轴（即1）
            crdPrm.profile2 = 2; // 规划器2(即profile2)对应到X轴（即2）
            crdPrm.setOriginFlag = 1; // 表示需要指定坐标系的原点坐标的规划位置
            crdPrm.originPos1 = 0; // 坐标系的原点坐标的规划位置为（100, 100）
            crdPrm.originPos2 = 0;
            // 建立1号坐标系，设置坐标系参数
            rtn = gts.mc.GT_SetCrdPrm(1, ref crdPrm);
        }
        // 坐标系运动状态查询变量
        short run;
        // 坐标系运动完成段查询变量
        int segment;
        // 坐标系的缓存区剩余空间查询变量
        int space;
        /// <summary>
        /// 直线插补
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnLine_Click(object sender, EventArgs e)
        {
            // 即将把数据存入坐标系1的FIFO0中，所以要首先清除此缓存区中的数据
            rtn = gts.mc.GT_CrdClear(1,0);
            // 向缓存区写入第一段插补数据
            rtn = gts.mc.GT_LnXY(
                1, // 该插补段的坐标系是坐标系1
                20000, 10000, // 该插补段的终点坐标(20000, 0)
                100, // 该插补段的目标速度：100pulse/ms
                0.1, // 插补段的加速度：0.1pulse/ms^2
                0, // 终点速度为0
                0); // 向坐标系1的FIFO0缓存区传递该直线插补数据
            // 向缓存区写入第二段插补数据
            rtn = gts.mc.GT_LnXY(
                1, // 该插补段的坐标系是坐标系1
                20000, -5000, // 该插补段的终点坐标(20000, 0)
                100, // 该插补段的目标速度：100pulse/ms
                0.1, // 插补段的加速度：0.1pulse/ms^2
                0, // 终点速度为0
                0); // 向坐标系1的FIFO0缓存区传递该直线插补数据
            // 查询坐标系1的FIFO0所剩余的空间
            rtn = gts.mc.GT_CrdSpace(1,out space,0);
            // 启动坐标系1的FIFO0的插补运动
            rtn = gts.mc.GT_CrdStart(1, 0);
            // 等待运动完成
            rtn = gts.mc.GT_CrdStatus(1, out run, out segment, 0);
            do
            {
                Console.WriteLine("运动中……");
                // 查询坐标系1的FIFO的插补运动状态
                rtn = gts.mc.GT_CrdStatus(
                1, // 坐标系是坐标系1
                out run, // 读取插补运动状态
                out segment, // 读取当前已经完成的插补段数
                0); // 查询坐标系1的FIFO0缓存区
            // 坐标系在运动, 查询到的run的值为1
            } while (run == 1);
            Console.WriteLine("运动完成");
        }

        /// <summary>
        /// 弧插补
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnArc_Click(object sender, EventArgs e)
        {
            // 即将把数据存入坐标系1的FIFO0中，所以要首先清除此缓存区中的数据
            rtn = gts.mc.GT_CrdClear(1, 0);
            // 弧插补
            rtn = gts.mc.GT_ArcXYR(
                1, // 坐标系是坐标系1
                0, 20000, // 该圆弧的终点坐标(0, 20000)
                20000, // 半径：20000pulse
                1, // 该圆弧是逆时针圆弧
                100, // 该插补段的目标速度：100pulse/ms
                0.1, // 该插补段的加速度：0.1pulse/ms^2
                0, // 终点速度为0
                0); // 向坐标系1的FIFO0缓存区传递该直线插补数据
            // 查询坐标系1的FIFO0所剩余的空间
            rtn = gts.mc.GT_CrdSpace(1, out space, 0);
            // 启动坐标系1的FIFO0的插补运动
            rtn = gts.mc.GT_CrdStart(1, 0);
            // 等待运动完成
            rtn = gts.mc.GT_CrdStatus(1, out run, out segment, 0);
            do
            {
                Console.WriteLine("运动中……");
                // 查询坐标系1的FIFO的插补运动状态
                rtn = gts.mc.GT_CrdStatus(
                1, // 坐标系是坐标系1
                out run, // 读取插补运动状态
                out segment, // 读取当前已经完成的插补段数
                0); // 查询坐标系1的FIFO0缓存区
                    // 坐标系在运动, 查询到的run的值为1
            } while (run == 1);
            Console.WriteLine("运动完成");
        }
        /// <summary>
        /// 圆插补
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnCircle_Click(object sender, EventArgs e)
        {
            // 即将把数据存入坐标系1的FIFO0中，所以要首先清除此缓存区中的数据
            rtn = gts.mc.GT_CrdClear(1, 0);
            // 圆插补
            rtn = gts.mc.GT_ArcXYC(
                1, // 坐标系是坐标系1
                0, 0, // 该圆弧的终点坐标(0, 0)
                -10000, 0, // 圆弧插补的圆心相对于起点位置的偏移量(-10000, 0)
                0, // 该圆弧是顺时针圆弧
                100, // 该插补段的目标速度：100pulse/ms
                0.1, // 该插补段的加速度：0.1pulse/ms^2
                0, // 终点速度为0
                0);
            // 查询坐标系1的FIFO0所剩余的空间
            rtn = gts.mc.GT_CrdSpace(1, out space, 0);
            // 启动坐标系1的FIFO0的插补运动
            rtn = gts.mc.GT_CrdStart(1, 0);
            // 等待运动完成
            rtn = gts.mc.GT_CrdStatus(1, out run, out segment, 0);
            do
            {
                Console.WriteLine("运动中……");
                // 查询坐标系1的FIFO的插补运动状态
                rtn = gts.mc.GT_CrdStatus(
                1, // 坐标系是坐标系1
                out run, // 读取插补运动状态
                out segment, // 读取当前已经完成的插补段数
                0); // 查询坐标系1的FIFO0缓存区
                    // 坐标系在运动, 查询到的run的值为1
            } while (run == 1);
            Console.WriteLine("运动完成");
        }
        #endregion


        #region DO操作
        // 气缸下降
        private void btnQGXJ_Click(object sender, EventArgs e)
        {
            rtn = gts.mc.GT_SetDoBit(gts.mc.MC_GPO, 1, 1);
            rtn = gts.mc.GT_SetDoBit(gts.mc.MC_GPO, 2, 0);
        }
        // 气缸上升
        private void btnQGSS_Click(object sender, EventArgs e)
        {
            rtn = gts.mc.GT_SetDoBit(gts.mc.MC_GPO, 1, 0);
            rtn = gts.mc.GT_SetDoBit(gts.mc.MC_GPO, 2, 1);
        }
        // 气缸松开
        private void btnQGSK_Click(object sender, EventArgs e)
        {
            rtn = gts.mc.GT_SetDoBit(gts.mc.MC_GPO, 3, 1);
            rtn = gts.mc.GT_SetDoBit(gts.mc.MC_GPO, 4, 0);
        }
        // 气缸加紧
        private void btnQGJJ_Click(object sender, EventArgs e)
        {
            rtn = gts.mc.GT_SetDoBit(gts.mc.MC_GPO, 3, 0);
            rtn = gts.mc.GT_SetDoBit(gts.mc.MC_GPO, 4, 1);
        }
        // 传送带
        private void btnCSD_Click(object sender, EventArgs e)
        {
            int pValue;
            rtn = gts.mc.GT_GetDo(gts.mc.MC_GPO, out pValue);
            if (Convert.ToBoolean(pValue & (1 << 4)))
            {
                rtn = gts.mc.GT_SetDoBit(gts.mc.MC_GPO, 5, 0);
            }
            else
            {
                rtn = gts.mc.GT_SetDoBit(gts.mc.MC_GPO, 5, 1);
            }
        }
        #endregion


        #region 自动码垛
        /// <summary>
        /// 回原点
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnGoHome_Click(object sender, EventArgs e)
        {
            // 两个轴都回原点
            goHome(1, 50, 10, 0.2, 0.2);
            goHome(2, 50, 10, 0.2, 0.2);
        }

        /// <summary>
        /// 封装回原点的方法
        /// </summary>
        /// <param name="axisIdx">轴号</param>
        /// <param name="velHigh">最高速度</param>
        /// <param name="velLow">最低速度</param>
        /// <param name="acc">加速度</param>
        /// <param name="dec">减速度</param>
        private void goHome(short axisIdx, double velHigh, double velLow, double acc, double dec)
        {
            // 清除状态和位置清零
            rtn = gts.mc.GT_ClrSts(axisIdx, 1);
            rtn = gts.mc.GT_ZeroPos(axisIdx, 1);

            // 设置 Smart Home 回原点参数
            gts.mc.THomePrm tHomePrm;
            // 读取设置到控制器的Smart Home回原点参数
            rtn = gts.mc.GT_GetHomePrm(axisIdx, out tHomePrm);
            // 设置Smart Home回原点参数
            tHomePrm.mode = gts.mc.HOME_MODE_LIMIT_HOME; // 回原点模式
            tHomePrm.moveDir = -1; // 设置启动搜索原点时的运动方向（如果回原点运动包含搜索 Limit 则为搜索 Limit 的运动方向）：-1 - 负方向，1 - 正方向
            tHomePrm.indexDir = 1; // 设置搜索 Index 的运动方向：-1-负方向，1 - 正方向，在限位 + Index 回原点模式下 moveDir 与indexDir 应该相异
            tHomePrm.edge = 1;// 设置捕获沿：0-下降沿，1-上升沿
            tHomePrm.velHigh = velHigh; // 回原点运动的高速速度（单位：pulse/ms）
            tHomePrm.velLow = velLow; // 回原点运动的低速速度（单位：pulse/ms）
            tHomePrm.acc = acc; // 回原点运动的加速度（单位：pulse/ms^2）
            tHomePrm.dec = dec; // 回原点运动的减速度（单位：pulse/ms^2）
            tHomePrm.smoothTime = 10; // 回原点运动的平滑时间：取值[0,50]，单位：ms，具体含义与 GTS 系列控制器点位运动相似
            tHomePrm.homeOffset = 0; // 最终停止的位置相对于原点的偏移量
            tHomePrm.searchHomeDistance = 0; // 设定的搜索 Home 的搜索范围，0 表示搜索距离为 805306368
            // 启动 Smart Home 回原点
            rtn = gts.mc.GT_GoHome(axisIdx, ref tHomePrm);

            // 获取回原点状态
            gts.mc.THomeStatus tHomeSts;
            Thread threadHome = new Thread(() => {
                do
                {
                    rtn = gts.mc.GT_GetHomeStatus(axisIdx, out tHomeSts);
                    Console.WriteLine("正在回原点");
                    Thread.Sleep(100);
                    // 1 表示正在运动  0 表示停止运动
                } while (tHomeSts.run == 1);
                Thread.Sleep(500);
                Console.WriteLine("回原点完成");
                rtn = gts.mc.GT_ZeroPos(axisIdx, 1);
            })
            { IsBackground = true };
            threadHome.Start();
        }

        /// <summary>
        /// 自动码垛
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnRun_Click(object sender, EventArgs e)
        {
            // 物料大小（包含间隙）
            int size = 60000;
            // 获取行和列
            int row = Convert.ToInt32(txtRow.Text);
            int col = Convert.ToInt32(txtCol.Text);
            int count = row * col;
            // 获取取料位
            int quLiaoX = Convert.ToInt32(txtQLX.Text);
            int quLiaoY = Convert.ToInt32(txtQLY.Text);
            // 获取放料位
            int fangLiaoX = Convert.ToInt32(txtFLX.Text);
            int fangLiaoY = Convert.ToInt32(txtFLY.Text);
            // 标识当前码第几个
            int n = 0;

            // 自动码垛
            Thread thread = new Thread(() =>
            {
                // 第一步：准备工作
                rtn = gts.mc.GT_SetDoBit(gts.mc.MC_GPO, 1, 0);
                rtn = gts.mc.GT_SetDoBit(gts.mc.MC_GPO, 2, 1); // 气缸上升
                rtn = gts.mc.GT_SetDoBit(gts.mc.MC_GPO, 3, 1);
                rtn = gts.mc.GT_SetDoBit(gts.mc.MC_GPO, 4, 0); // 气缸松开
                rtn = gts.mc.GT_SetDoBit(gts.mc.MC_GPO, 5, 0); // 开启传送带

                // 第二步：循环码垛
                while (n < count)
                {
                    // 第三步：计算第n个放置的位置（九宫格计算）
                    int x = fangLiaoX + (size + 10000) * (n / col);
                    int y = fangLiaoY + size * (n % col);

                    // 第四步：移动抓手到取料位
                    rtn = gts.mc.GT_CrdClear(1, 0);
                    rtn = gts.mc.GT_LnXY(1, quLiaoX, quLiaoY, 80, 0.2, 0, 0);
                    rtn = gts.mc.GT_CrdStart(1, 0);

                    // 第五步：等待插补运动完成
                    short pRun;
                    int pSegment;
                    do
                    {
                        gts.mc.GT_CrdStatus(1, out pRun, out pSegment, 0);
                        Thread.Sleep(100);
                    } while (pRun == 1);
                    Thread.Sleep(500);

                    // 第六步：气缸下降
                    Thread.Sleep(500);

                    // 第七步：气缸夹紧
                    Thread.Sleep(500);

                    // 第八步：气缸上升
                    Thread.Sleep(500);

                    // 第九步：移动抓手到放料位
                    rtn = gts.mc.GT_CrdClear(1, 0);
                    rtn = gts.mc.GT_LnXY(1, x, y, 80, 0.2, 0, 0);
                    rtn = gts.mc.GT_CrdStart(1, 0);
                    Thread.Sleep(500);

                    // 第十步：等待插补运动完成
                    do
                    {
                        gts.mc.GT_CrdStatus(1, out pRun, out pSegment, 0);
                        Thread.Sleep(100);
                    } while (pRun == 1);
                    Thread.Sleep(500);

                    // 第十一步：气缸下降
                    Thread.Sleep(500);

                    // 第十二步：气缸松开
                    Thread.Sleep(500);

                    // 第十三步：气缸上升
                    Thread.Sleep(500);

                    // 第十四步：让n累加
                    n++;
                }

                // 最后一步：关闭传送带
                rtn = gts.mc.GT_SetDoBit(gts.mc.MC_GPO, 5, 1); 

            }) { IsBackground = true};
            thread.Start();
        }
        #endregion
    }
}
